package hydrap2p.servicemanager;

import hydrap2p.ServiceLocation;
import hydrap2p.helper.Pinger;
import hydrap2p.helper.ThriftServerThread;
import hydrap2p.helper.ThriftUtils;
import hydrap2p.helper.Utility;
import hydrap2p.modulemanager.ModuleControl;
import hydrap2p.servicemanager.listen.Listeners;
import hydrap2p.servicemanager.listen.ThriftListener;
import hydrap2p.speedlistener.SpeedListener;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.thrift.TException;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;

public class ServiceManagerServer implements ServiceManager.Iface {

	private ServiceController controller;
	private ServerSocket socket;
	private ServiceManagerCTL modulecontrol;
	private TServer server;
	
	public ServiceManagerServer(ServiceController controller) throws IOException {
		this.controller = controller;
		
		initServerSocket();
	}
	
	private void initServerSocket() throws IOException
	{
		int port;
		try {
			port=Utility.getManagerPort();
		} catch (FileNotFoundException e) {
			System.err.println("Could not load configuration: "+e.getLocalizedMessage());
			System.err.println("Please provide a valid configuration of where to locate the Manager, and restart this program");
			return;
		}

		System.err.println("Starting ServiceManager");
		
		// bind to the socket
		socket = new ServerSocket();
		socket.bind(new InetSocketAddress("localhost",port));
		TServerTransport tsocket = new TServerSocket(socket);
		TThreadPoolServer.Args args = new TThreadPoolServer.Args(tsocket);
		args.processor(new ServiceManager.Processor(this));
		server = new TThreadPoolServer(args);
		System.err.println("Bound ServiceManager to port "+port);
		
		try {
			controller.setServiceLocation("ServiceManager", new ServiceLocation("localhost", socket.getLocalPort()));
		} catch (NameTaken e) {
			// Highly doubtful
		}
		
		// start ServiceManagerCTL
		new Thread() {
			public void run()
			{
				modulecontrol=new ServiceManagerCTL();
				try {
					ThriftServerThread temp=ThriftUtils.initThriftServer(new ModuleControl.Processor(modulecontrol), "ServiceManagerCTL");
					temp.start();
				} catch (NameTaken e) {
					// Highly unlikely
				} catch (BindException e) {
					// Highly unlikely
				} catch (IOException e) {
					// Highly unlikely
				}
			}
		}.start();
	}
	
	public void start()
	{
		server.serve();
	}

	@Override
	public void ping() throws TException {
	}

	@Override
	public List<String> supportedInterfaces() throws TException {
		return Arrays.asList("Service", "ServiceManager");
	}

	@Override
	public void delServiceLocation(String arg0) throws TException {
		controller.delServiceLocation(arg0);
	}

	@Override
	public Map<String, ServiceLocation> getAllServiceLocations()
			throws TException {
		return controller.getAllServiceLocations();
	}

	@Override
	public ServiceLocation getServiceLocation(String arg0) throws TException {
		return controller.getServiceLocation(arg0);
	}

	@Override
	public void setServiceLocation(String arg0, ServiceLocation arg1)
			throws NameTaken, TException {
		controller.setServiceLocation(arg0, arg1);
		if (arg0.equals("ModuleManager"))
		{
			new MyPinger("ModuleManager", arg1);
		}
	}

	@Override
	public boolean subscribe(ServiceLocation arg0) {
		try {
			System.out.println("Incoming ServiceListener subscription for "+arg0);
			ThriftListener temp=new ThriftListener(arg0);
			try {
				temp.connect();
			} catch (Exception e) {
				System.out.println("Could not connect to ServiceListener");
				e.printStackTrace();
				// Invalid listener
				return false;
			}
			Listeners.addListener(temp);
			return true;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}

	@Override
	public void unsubscribe(ServiceLocation arg0) throws TException {
		ThriftListener temp=new ThriftListener(arg0);
		Listeners.delListener(temp);
		
	}
	
	private class MyPinger extends Pinger
	{
		public MyPinger(String name, ServiceLocation location) {
			super(name, location, 500, 3000);
		}

		@Override
		public void failure(String name) {
			System.out.println("Could not ping ModuleManager, shutting down");
			System.exit(0);
		}
		
	}

}
